Repository navigation
fix(tree): take a retired Bot out of the tree and re-attach its children - #2436
Conversation
|
@Birfy review request for this exact tip: Ready at this tip: red-first on a clean
|
archive_bot writes fields['flybot-tree'].retired = true, but the model
still counted the Bot as a member: snapshot() and isMember() kept it, so
it held its parent's maxChildren slot, appeared in list_children, and its
own children stayed parked under a Bot the host refuses to deliver to
("the target Bot was retired", core/src/host/hooks.js:404).
Skip a retired record in the model, and handle the retired field change by
handing the Bot's children to its own parent (the current Main for a
retired root) with prompts refreshed, the way onBotRemoved already does
for deletion. forestOf and adoptUnattached already skipped retired records,
so this makes snapshot()/isMember() consistent with them.
The new parent is resolved before any parent is written: adoptChief()
repairs and refreshes, so running it while the children are still orphans
recorded a role-based re-attachment the move then overwrote (undo answers
"moved again since") and could leave a level over maxChildren until the
second repair. With no Main, ensureRoot() runs before the move too, so a
child is never read as the existing root and never ends up its own parent.
Fixes ThinkFlowLab#2430
8fc2edd to
c3da0e6
Compare
|
@Birfy review request for this exact tip: What changed since the previously named tip
Gates at the tip: red-first on a clean Please review |
|
Verdict: PASS at c3da0e6 Ran at this tip: Not run:
Review points, all against the tip tarball:
|
fix(tree): take a retired Bot out of the tree and re-attach its children
Fixes #2430
Owner: Dev-5
Reviewer: Birfy
Written-by: Dev-5
Branch: dev/5-retired-bot-tree
Tip: c3da0e6
Base: 16d1645
Diff: 4 files, +116/−4
Tier: T2
why:
plugins/tree/src/tree.jsandplugins/tree/test/tree.test.jsare T2 under the liverisk-rules.jsonat this base;changelog.d/**is T1,docs/**is T0Docs-changed: docs/subsystems/tree.md (the retire paragraph now says the model itself skips the record and re-attaches its children)
What was wrong
archive_botretires a Bot by writingfields['flybot-tree'].retired = true(core/src/host/roster.js:585) and hiding it, and the Main-Bot prompt promises the retired Bot "leaves the tree (the slot is free again)" (core/src/host/prompt.js:46). The model did not read that field:plugins/tree/src/tree.js:27snapshot()collected every Bot withparent/treeRole, retired or not, so the retired Bot stayed a node: its parent still listed it inlist_children, its name stayed in its children's prompt trace, and it still counted againstmaxChildreninplugins/tree/src/limits.js:21.isMember()(plugins/tree/src/tree.js:271) agreed, so a retired parent still passed the membership guard ofcreate_child_bot.TREE_KEYS(plugins/tree/src/tree.js:18) had noretired, soonFieldChangeignored the write and the retired Bot's children keptparent = <retired id>: a dangling subtree the root'slist_childrencannot see, whose messages to that parent are refused withthe target Bot was retired(core/src/host/hooks.js:404), while their prompt still readChief → Retired → Child.forestOf(plugins/tree/src/forest.js:16) andadoptUnattached(plugins/tree/src/tree.js:153) already skipped retired records, so the model was the inconsistent half.What changed
plugins/tree/src/tree.js(+57/−3):isRetired()skips a retired record insnapshot()andisMember().onFieldChangenow handles theretiredkey: on the transition totrueit hands the Bot's children to its own parent (the current Main when the retired Bot was a root, mirroringonBotRemoved) and refreshes every prompt; retiring a Bot with no children only refreshes prompts.retiredchanges are still emitted to the subscribed consumers.detachRetired()resolves the new parent before writing anyparent, then assigns once and repairs once. CallingadoptChief()while the children were still orphans made itsorganizer.repair()record a role-based re-attachment this move then overwrote (undo answers "moved again since") and could leave a level overmaxChildrenuntil the second repair; now the repair sees the final parents. With no Main,ensureRoot()runs before the move, so it cannot read one of the orphans as the root that already exists — the branch that used to leave a child pointing at itself.plugins/tree/test/tree.test.js(+57/−0): four tests, one extended and three added. A retired parent is absent fromsnapshot(),isMember()andlist_children; its child moves to the grandparent, its prompt readsChief → Dev, and the root's prompt no longer lists the retired Bot. A grandchild (Chief → Work → Dev → Junior) keeps its own parent whenWorkis retired and still readsChief → Dev → Junior. A synthetic no-Main state (retired root, every Bot de-Mained) creates exactly one fresh Root, puts the orphans under it, and leaves no node whoseparentis itself. And retiring a child undermaxChildren: 1makes the slot reusable for a new child.docs/subsystems/tree.md(+1/−1): the retire paragraph now namessnapshot()/isMember()and the child hand-off.changelog.d/2430-tree-retired.fixed.md(+1).onBotRemoved()(the deletion path, #446) keeps its original ordering; it is not part of this change and is left alone here.What I ran
16d16456in a detached worktree with only this test file:node --import ./test/resolve.mjs --test --test-name-pattern "retir" test/tree.test.js→ 4 tests, 0 pass, 4 fail, while the three older tests matching the pattern pass (so the new ones are not vacuous). The failures are the defect itself:snapshot()[retiredWork]still returns the Bot, the grandchild still hangs under the retiredWork(expected 'bot1', actual 'bot2'), the no-Main case still reads the retired root as the root (expected 'bot1', actual 'bot1'), and the slot is not reusable. With the fix the same file is 136/136.TZ=UTC ARCH_DOCS_BASE=16d16456 npm run verify— exit 0, 4847 tests, 0 fail (core 2851, social 574, tree 136, timeline 141, workflow 411,.github671, interop 37, live-e2e 26); nonot okline in the log.ARCH_DOCS_BASE=16d16456 node scripts/check-arch-docs.mjsat the committed tip:arch-docs: OK (4 changed file(s)).node .github/scripts/changelog.mjs check: 35 fragments well formed.Not run
core/index.jsorcore/src/host/changed.The PR tip is the push helper's commit over the same tree the checks ran on.
Checkpoint
{ "pr": 2436, "claim": "Dev-5", "issue": 2430, "summary": "archive_bot retires a Bot with fields['flybot-tree'].retired = true, but the model still counted it: it held its parent's maxChildren slot, showed in list_children and its children's prompt trace, and its children stayed under a Bot the host refuses to deliver to. snapshot() and isMember() now skip it, and the retired change hands its children to its own parent (the current Main for a retired root). The new parent is resolved before any parent is written, so adoption cannot record a move the hand-off overwrites; four tests cover membership, the root's list, a grandchild and no-Main re-root.", "files": ["plugins/tree/src/tree.js", "plugins/tree/test/tree.test.js", "docs/subsystems/tree.md", "changelog.d/2430-tree-retired.fixed.md"], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=16d16456 npm run verify; ARCH_DOCS_BASE=16d16456 node scripts/check-arch-docs.mjs", "result": "green", "at": "c3da0e6e3649157593baa0c817d18d682b78db00" }, "next": "none — ready for review" }